Commit 30cbd5d3 authored by Claude Paroz's avatar Claude Paroz
Browse files

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.
parent 15ba0d16
Loading
Loading
Loading
Loading
+15 −49
Original line number Diff line number Diff line
@@ -225,14 +225,12 @@ class Command(BaseCommand):
        try:
            # Get a list of already installed *models* so that references work right.
            tables = connection.introspection.table_names(cursor)
            seen_models = connection.introspection.installed_models(tables)
            created_models = set()
            pending_references = {}

            # Build the manifest of apps and models that are to be synchronized
            all_models = [
                (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()
                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:
                self.stdout.write("  Creating tables...\n")
            with transaction.atomic(using=connection.alias, savepoint=connection.features.can_rollback_ddl):
                deferred_sql = []
                for app_name, model_list in manifest.items():
                    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:
                            self.stdout.write(
                                "    Processing %s.%s model\n" % (app_name, model._meta.object_name)
                            )
                        sql, references = connection.creation.sql_create_model(model, no_style(), seen_models)
                        seen_models.add(model)
                        created_models.add(model)
                        for refto, refs in references.items():
                            pending_references.setdefault(refto, []).extend(refs)
                            if refto in seen_models:
                                sql.extend(
                                    connection.creation.sql_for_pending_references(
                                        refto, no_style(), pending_references,
                                    )
                                )
                        sql.extend(
                            connection.creation.sql_for_pending_references(
                                model, no_style(), pending_references
                            )
                        )
                        if self.verbosity >= 1 and sql:
                        with connection.schema_editor() as editor:
                            if self.verbosity >= 1:
                                self.stdout.write("    Creating table %s\n" % model._meta.db_table)
                        for statement in sql:
                            editor.create_model(model)
                            deferred_sql.extend(editor.deferred_sql)
                            editor.deferred_sql = []
                        created_models.add(model)

                if self.verbosity >= 1:
                    self.stdout.write("    Running deferred SQL...\n")
                for statement in deferred_sql:
                    cursor.execute(statement)
                        tables.append(connection.introspection.table_name_converter(model._meta.db_table))
        finally:
            cursor.close()

@@ -321,31 +312,6 @@ class Command(BaseCommand):
                                    "    No custom SQL for %s.%s model\n" %
                                    (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:
            cursor.close()

+5 −0
Original line number Diff line number Diff line
import hashlib
import sys
import time
import warnings

from django.conf import settings
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_bytes
from django.utils.six.moves import input
from django.utils.six import StringIO
@@ -192,6 +194,9 @@ class BaseDatabaseCreation(object):
        """
        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:
            return []
        output = []
+18 −11
Original line number Diff line number Diff line
@@ -123,9 +123,10 @@ class BaseDatabaseSchemaEditor(object):
        # Work out nullability
        null = field.null
        # 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)
        if include_default and default_value is not None:
        if include_default:
            default_value = self.effective_default(field)
            if default_value is not None:
                if self.connection.features.requires_literal_defaults:
                    # Some databases can't take defaults as a parameter (oracle)
                    # If this is the case, the individual schema backend should
@@ -247,6 +248,7 @@ class BaseDatabaseSchemaEditor(object):
                autoinc_sql = self.connection.ops.autoinc_sql(model._meta.db_table, field.column)
                if autoinc_sql:
                    self.deferred_sql.extend(autoinc_sql)

        # Add any unique_togethers
        for fields in model._meta.unique_together:
            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),
            "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)
        self.deferred_sql.extend(self._model_indexes_sql(model))
+4 −3
Original line number Diff line number Diff line
@@ -111,9 +111,10 @@ class SQLiteTests(TestCase):
        Check that auto_increment fields are created with the AUTOINCREMENT
        keyword in order to be monotonically increasing. Refs #10164.
        """
        statements = connection.creation.sql_create_model(models.Square,
            style=no_style())
        match = re.search('"id" ([^,]+),', statements[0][0])
        with connection.schema_editor(collect_sql=True) as editor:
            editor.create_model(models.Square)
            statements = editor.collected_sql
        match = re.search('"id" ([^,]+),', statements[0])
        self.assertIsNotNone(match)
        self.assertEqual('integer NOT NULL PRIMARY KEY AUTOINCREMENT',
            match.group(1), "Wrong SQL used to create an auto-increment "
+8 −2
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from __future__ import unicode_literals

import re
import unittest
import warnings

from django.apps import apps
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.test import TestCase, override_settings
from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning

# See also initial_sql_regress for 'custom_sql_for_model' tests

@@ -67,6 +69,8 @@ class SQLCommandsTestCase(TestCase):

    def test_sql_indexes(self):
        app_config = apps.get_app_config('commands_sql')
        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
        self.assertIn(self.count_ddl(output, 'CREATE INDEX'), [3, 4])
@@ -79,6 +83,8 @@ class SQLCommandsTestCase(TestCase):

    def test_sql_all(self):
        app_config = apps.get_app_config('commands_sql')
        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)
Loading