Replaced DatabaseCreation sql methods by schema editor equivalents
Also used schema editor in migrate to sync unmigrated apps (sync_apps). Refs #22340. Thanks Tim Graham for the review.
This commit is contained in:
parent
15ba0d166f
commit
30cbd5d360
|
@ -225,14 +225,12 @@ class Command(BaseCommand):
|
||||||
try:
|
try:
|
||||||
# Get a list of already installed *models* so that references work right.
|
# Get a list of already installed *models* so that references work right.
|
||||||
tables = connection.introspection.table_names(cursor)
|
tables = connection.introspection.table_names(cursor)
|
||||||
seen_models = connection.introspection.installed_models(tables)
|
|
||||||
created_models = set()
|
created_models = set()
|
||||||
pending_references = {}
|
|
||||||
|
|
||||||
# Build the manifest of apps and models that are to be synchronized
|
# Build the manifest of apps and models that are to be synchronized
|
||||||
all_models = [
|
all_models = [
|
||||||
(app_config.label,
|
(app_config.label,
|
||||||
router.get_migratable_models(app_config, connection.alias, include_auto_created=True))
|
router.get_migratable_models(app_config, connection.alias, include_auto_created=False))
|
||||||
for app_config in apps.get_app_configs()
|
for app_config in apps.get_app_configs()
|
||||||
if app_config.models_module is not None and app_config.label in app_labels
|
if app_config.models_module is not None and app_config.label in app_labels
|
||||||
]
|
]
|
||||||
|
@ -256,34 +254,27 @@ class Command(BaseCommand):
|
||||||
if self.verbosity >= 1:
|
if self.verbosity >= 1:
|
||||||
self.stdout.write(" Creating tables...\n")
|
self.stdout.write(" Creating tables...\n")
|
||||||
with transaction.atomic(using=connection.alias, savepoint=connection.features.can_rollback_ddl):
|
with transaction.atomic(using=connection.alias, savepoint=connection.features.can_rollback_ddl):
|
||||||
|
deferred_sql = []
|
||||||
for app_name, model_list in manifest.items():
|
for app_name, model_list in manifest.items():
|
||||||
for model in model_list:
|
for model in model_list:
|
||||||
# Create the model's database table, if it doesn't already exist.
|
if model._meta.proxy or not model._meta.managed:
|
||||||
|
continue
|
||||||
if self.verbosity >= 3:
|
if self.verbosity >= 3:
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
" Processing %s.%s model\n" % (app_name, model._meta.object_name)
|
" Processing %s.%s model\n" % (app_name, model._meta.object_name)
|
||||||
)
|
)
|
||||||
sql, references = connection.creation.sql_create_model(model, no_style(), seen_models)
|
with connection.schema_editor() as editor:
|
||||||
seen_models.add(model)
|
if self.verbosity >= 1:
|
||||||
|
self.stdout.write(" Creating table %s\n" % model._meta.db_table)
|
||||||
|
editor.create_model(model)
|
||||||
|
deferred_sql.extend(editor.deferred_sql)
|
||||||
|
editor.deferred_sql = []
|
||||||
created_models.add(model)
|
created_models.add(model)
|
||||||
for refto, refs in references.items():
|
|
||||||
pending_references.setdefault(refto, []).extend(refs)
|
if self.verbosity >= 1:
|
||||||
if refto in seen_models:
|
self.stdout.write(" Running deferred SQL...\n")
|
||||||
sql.extend(
|
for statement in deferred_sql:
|
||||||
connection.creation.sql_for_pending_references(
|
cursor.execute(statement)
|
||||||
refto, no_style(), pending_references,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
sql.extend(
|
|
||||||
connection.creation.sql_for_pending_references(
|
|
||||||
model, no_style(), pending_references
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if self.verbosity >= 1 and sql:
|
|
||||||
self.stdout.write(" Creating table %s\n" % model._meta.db_table)
|
|
||||||
for statement in sql:
|
|
||||||
cursor.execute(statement)
|
|
||||||
tables.append(connection.introspection.table_name_converter(model._meta.db_table))
|
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
@ -321,31 +312,6 @@ class Command(BaseCommand):
|
||||||
" No custom SQL for %s.%s model\n" %
|
" No custom SQL for %s.%s model\n" %
|
||||||
(app_name, model._meta.object_name)
|
(app_name, model._meta.object_name)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.verbosity >= 1:
|
|
||||||
self.stdout.write(" Installing indexes...\n")
|
|
||||||
|
|
||||||
# Install SQL indices for all newly created models
|
|
||||||
for app_name, model_list in manifest.items():
|
|
||||||
for model in model_list:
|
|
||||||
if model in created_models:
|
|
||||||
index_sql = connection.creation.sql_indexes_for_model(model, no_style())
|
|
||||||
if index_sql:
|
|
||||||
if self.verbosity >= 2:
|
|
||||||
self.stdout.write(
|
|
||||||
" Installing index for %s.%s model\n" %
|
|
||||||
(app_name, model._meta.object_name)
|
|
||||||
)
|
|
||||||
savepoint = connection.features.can_rollback_ddl
|
|
||||||
try:
|
|
||||||
with transaction.atomic(using=connection.alias, savepoint=savepoint):
|
|
||||||
for sql in index_sql:
|
|
||||||
cursor.execute(sql)
|
|
||||||
except Exception as e:
|
|
||||||
self.stderr.write(
|
|
||||||
" Failed to install index for %s.%s model: %s\n" %
|
|
||||||
(app_name, model._meta.object_name, e)
|
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.six.moves import input
|
from django.utils.six.moves import input
|
||||||
from django.utils.six import StringIO
|
from django.utils.six import StringIO
|
||||||
|
@ -192,6 +194,9 @@ class BaseDatabaseCreation(object):
|
||||||
"""
|
"""
|
||||||
Returns the CREATE INDEX SQL statements for a single model.
|
Returns the CREATE INDEX SQL statements for a single model.
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("DatabaseCreation.sql_indexes_for_model is deprecated, "
|
||||||
|
"use the equivalent method of the schema editor instead.",
|
||||||
|
RemovedInDjango20Warning)
|
||||||
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
|
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
|
||||||
return []
|
return []
|
||||||
output = []
|
output = []
|
||||||
|
|
|
@ -123,17 +123,18 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
# Work out nullability
|
# Work out nullability
|
||||||
null = field.null
|
null = field.null
|
||||||
# If we were told to include a default value, do so
|
# If we were told to include a default value, do so
|
||||||
default_value = self.effective_default(field)
|
|
||||||
include_default = include_default and not self.skip_default(field)
|
include_default = include_default and not self.skip_default(field)
|
||||||
if include_default and default_value is not None:
|
if include_default:
|
||||||
if self.connection.features.requires_literal_defaults:
|
default_value = self.effective_default(field)
|
||||||
# Some databases can't take defaults as a parameter (oracle)
|
if default_value is not None:
|
||||||
# If this is the case, the individual schema backend should
|
if self.connection.features.requires_literal_defaults:
|
||||||
# implement prepare_default
|
# Some databases can't take defaults as a parameter (oracle)
|
||||||
sql += " DEFAULT %s" % self.prepare_default(default_value)
|
# If this is the case, the individual schema backend should
|
||||||
else:
|
# implement prepare_default
|
||||||
sql += " DEFAULT %s"
|
sql += " DEFAULT %s" % self.prepare_default(default_value)
|
||||||
params += [default_value]
|
else:
|
||||||
|
sql += " DEFAULT %s"
|
||||||
|
params += [default_value]
|
||||||
# Oracle treats the empty string ('') as null, so coerce the null
|
# Oracle treats the empty string ('') as null, so coerce the null
|
||||||
# option whenever '' is a possible value.
|
# option whenever '' is a possible value.
|
||||||
if (field.empty_strings_allowed and not field.primary_key and
|
if (field.empty_strings_allowed and not field.primary_key and
|
||||||
|
@ -247,6 +248,7 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
autoinc_sql = self.connection.ops.autoinc_sql(model._meta.db_table, field.column)
|
autoinc_sql = self.connection.ops.autoinc_sql(model._meta.db_table, field.column)
|
||||||
if autoinc_sql:
|
if autoinc_sql:
|
||||||
self.deferred_sql.extend(autoinc_sql)
|
self.deferred_sql.extend(autoinc_sql)
|
||||||
|
|
||||||
# Add any unique_togethers
|
# Add any unique_togethers
|
||||||
for fields in model._meta.unique_together:
|
for fields in model._meta.unique_together:
|
||||||
columns = [model._meta.get_field_by_name(field)[0].column for field in fields]
|
columns = [model._meta.get_field_by_name(field)[0].column for field in fields]
|
||||||
|
@ -258,7 +260,12 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
"table": self.quote_name(model._meta.db_table),
|
"table": self.quote_name(model._meta.db_table),
|
||||||
"definition": ", ".join(column_sqls)
|
"definition": ", ".join(column_sqls)
|
||||||
}
|
}
|
||||||
self.execute(sql, params)
|
if model._meta.db_tablespace:
|
||||||
|
tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace)
|
||||||
|
if tablespace_sql:
|
||||||
|
sql += ' ' + tablespace_sql
|
||||||
|
# Prevent using [] as params, in the case a literal '%' is used in the definition
|
||||||
|
self.execute(sql, params or None)
|
||||||
|
|
||||||
# Add any field index and index_together's (deferred as SQLite3 _remake_table needs it)
|
# Add any field index and index_together's (deferred as SQLite3 _remake_table needs it)
|
||||||
self.deferred_sql.extend(self._model_indexes_sql(model))
|
self.deferred_sql.extend(self._model_indexes_sql(model))
|
||||||
|
|
|
@ -111,9 +111,10 @@ class SQLiteTests(TestCase):
|
||||||
Check that auto_increment fields are created with the AUTOINCREMENT
|
Check that auto_increment fields are created with the AUTOINCREMENT
|
||||||
keyword in order to be monotonically increasing. Refs #10164.
|
keyword in order to be monotonically increasing. Refs #10164.
|
||||||
"""
|
"""
|
||||||
statements = connection.creation.sql_create_model(models.Square,
|
with connection.schema_editor(collect_sql=True) as editor:
|
||||||
style=no_style())
|
editor.create_model(models.Square)
|
||||||
match = re.search('"id" ([^,]+),', statements[0][0])
|
statements = editor.collected_sql
|
||||||
|
match = re.search('"id" ([^,]+),', statements[0])
|
||||||
self.assertIsNotNone(match)
|
self.assertIsNotNone(match)
|
||||||
self.assertEqual('integer NOT NULL PRIMARY KEY AUTOINCREMENT',
|
self.assertEqual('integer NOT NULL PRIMARY KEY AUTOINCREMENT',
|
||||||
match.group(1), "Wrong SQL used to create an auto-increment "
|
match.group(1), "Wrong SQL used to create an auto-increment "
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.core.management.color import no_style
|
from django.core.management.color import no_style
|
||||||
|
@ -10,6 +11,7 @@ from django.core.management.sql import (sql_create, sql_delete, sql_indexes,
|
||||||
from django.db import connections, DEFAULT_DB_ALIAS
|
from django.db import connections, DEFAULT_DB_ALIAS
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
|
|
||||||
# See also initial_sql_regress for 'custom_sql_for_model' tests
|
# See also initial_sql_regress for 'custom_sql_for_model' tests
|
||||||
|
|
||||||
|
@ -67,7 +69,9 @@ class SQLCommandsTestCase(TestCase):
|
||||||
|
|
||||||
def test_sql_indexes(self):
|
def test_sql_indexes(self):
|
||||||
app_config = apps.get_app_config('commands_sql')
|
app_config = apps.get_app_config('commands_sql')
|
||||||
output = sql_indexes(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", category=RemovedInDjango20Warning)
|
||||||
|
output = sql_indexes(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
|
||||||
# PostgreSQL creates one additional index for CharField
|
# PostgreSQL creates one additional index for CharField
|
||||||
self.assertIn(self.count_ddl(output, 'CREATE INDEX'), [3, 4])
|
self.assertIn(self.count_ddl(output, 'CREATE INDEX'), [3, 4])
|
||||||
|
|
||||||
|
@ -79,7 +83,9 @@ class SQLCommandsTestCase(TestCase):
|
||||||
|
|
||||||
def test_sql_all(self):
|
def test_sql_all(self):
|
||||||
app_config = apps.get_app_config('commands_sql')
|
app_config = apps.get_app_config('commands_sql')
|
||||||
output = sql_all(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", category=RemovedInDjango20Warning)
|
||||||
|
output = sql_all(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
|
||||||
|
|
||||||
self.assertEqual(self.count_ddl(output, 'CREATE TABLE'), 3)
|
self.assertEqual(self.count_ddl(output, 'CREATE TABLE'), 3)
|
||||||
# PostgreSQL creates one additional index for CharField
|
# PostgreSQL creates one additional index for CharField
|
||||||
|
|
|
@ -3,7 +3,6 @@ from __future__ import unicode_literals
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.core.management.color import no_style
|
|
||||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||||
|
|
||||||
from .models.tablespaces import (Article, ArticleRef, Authors, Reviewers,
|
from .models.tablespaces import (Article, ArticleRef, Authors, Reviewers,
|
||||||
|
@ -11,13 +10,13 @@ from .models.tablespaces import (Article, ArticleRef, Authors, Reviewers,
|
||||||
|
|
||||||
|
|
||||||
def sql_for_table(model):
|
def sql_for_table(model):
|
||||||
return '\n'.join(connection.creation.sql_create_model(model,
|
with connection.schema_editor(collect_sql=True) as editor:
|
||||||
no_style())[0])
|
editor.create_model(model)
|
||||||
|
return editor.collected_sql[0]
|
||||||
|
|
||||||
|
|
||||||
def sql_for_index(model):
|
def sql_for_index(model):
|
||||||
return '\n'.join(connection.creation.sql_indexes_for_model(model,
|
return '\n'.join(connection.schema_editor()._model_indexes_sql(model))
|
||||||
no_style()))
|
|
||||||
|
|
||||||
|
|
||||||
# We can't test the DEFAULT_TABLESPACE and DEFAULT_INDEX_TABLESPACE settings
|
# We can't test the DEFAULT_TABLESPACE and DEFAULT_INDEX_TABLESPACE settings
|
||||||
|
|
Loading…
Reference in New Issue