Repository: django/django

Issue:
# Doc'd return values of as_sql() for Func and query expressions.

Mostly to make it clear that `params` may be a list or tuple.

Implementation patch:
diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py
index 268f669ba257..62ad29e43d8a 100644
--- a/django/core/management/commands/migrate.py
+++ b/django/core/management/commands/migrate.py
@@ -6,6 +6,7 @@
 from django.core.management.base import BaseCommand, CommandError, no_translations
 from django.core.management.sql import emit_post_migrate_signal, emit_pre_migrate_signal
 from django.db import DEFAULT_DB_ALIAS, connections, router
+from django.db.backends.utils import truncate_name
 from django.db.migrations.autodetector import MigrationAutodetector
 from django.db.migrations.executor import MigrationExecutor
 from django.db.migrations.loader import AmbiguityError
@@ -447,8 +448,9 @@ def sync_apps(self, connection, app_labels):
         def model_installed(model):
             opts = model._meta
             converter = connection.introspection.identifier_converter
+            max_name_length = connection.ops.max_name_length()
             return not (
-                (converter(opts.db_table) in tables)
+                (converter(truncate_name(opts.db_table, max_name_length)) in tables)
                 or (
                     opts.auto_created
                     and converter(opts.auto_created._meta.db_table) in tables


Test patch:
diff --git a/tests/migrations/migrations_test_apps/unmigrated_app_syncdb/models.py b/tests/migrations/migrations_test_apps/unmigrated_app_syncdb/models.py
index 9f3179cd0d6d..c539b20d1184 100644
--- a/tests/migrations/migrations_test_apps/unmigrated_app_syncdb/models.py
+++ b/tests/migrations/migrations_test_apps/unmigrated_app_syncdb/models.py
@@ -7,3 +7,8 @@ class Classroom(models.Model):
 
 class Lesson(models.Model):
     classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE)
+
+
+class VeryLongNameModel(models.Model):
+    class Meta:
+        db_table = "long_db_table_that_should_be_truncated_before_checking"
diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py
index 6a0d9bd6d28d..e9929c1eafa9 100644
--- a/tests/migrations/test_commands.py
+++ b/tests/migrations/test_commands.py
@@ -25,6 +25,7 @@
     connections,
     models,
 )
+from django.db.backends.base.introspection import BaseDatabaseIntrospection
 from django.db.backends.base.schema import BaseDatabaseSchemaEditor
 from django.db.backends.utils import truncate_name
 from django.db.migrations.autodetector import MigrationAutodetector
@@ -1222,10 +1223,10 @@ def test_migrate_syncdb_deferred_sql_executed_with_schemaeditor(self):
             create_table_count = len(
                 [call for call in execute.mock_calls if "CREATE TABLE" in str(call)]
             )
-            self.assertEqual(create_table_count, 2)
+            self.assertEqual(create_table_count, 3)
             # There's at least one deferred SQL for creating the foreign key
             # index.
-            self.assertGreater(len(execute.mock_calls), 2)
+            self.assertGreater(len(execute.mock_calls), 3)
         stdout = stdout.getvalue()
         self.assertIn("Synchronize unmigrated apps: unmigrated_app_syncdb", stdout)
         self.assertIn("Creating tables...", stdout)
@@ -1259,8 +1260,54 @@ def test_migrate_syncdb_app_label(self):
             create_table_count = len(
                 [call for call in execute.mock_calls if "CREATE TABLE" in str(call)]
             )
-            self.assertEqual(create_table_count, 2)
+            self.assertEqual(create_table_count, 3)
+            self.assertGreater(len(execute.mock_calls), 3)
+            self.assertIn(
+                "Synchronize unmigrated app: unmigrated_app_syncdb", stdout.getvalue()
+            )
+
+    @override_settings(
+        INSTALLED_APPS=[
+            "migrations.migrations_test_apps.unmigrated_app_syncdb",
+            "migrations.migrations_test_apps.unmigrated_app_simple",
+        ]
+    )
+    def test_migrate_syncdb_installed_truncated_db_model(self):
+        """
+        Running migrate --run-syncdb doesn't try to create models with long
+        truncated name if already exist.
+        """
+        with connection.cursor() as cursor:
+            mock_existing_tables = connection.introspection.table_names(cursor)
+        # Add truncated name for the VeryLongNameModel to the list of
+        # existing table names.
+        table_name = truncate_name(
+            "long_db_table_that_should_be_truncated_before_checking",
+            connection.ops.max_name_length(),
+        )
+        mock_existing_tables.append(table_name)
+        stdout = io.StringIO()
+        with (
+            mock.patch.object(BaseDatabaseSchemaEditor, "execute") as execute,
+            mock.patch.object(
+                BaseDatabaseIntrospection,
+                "table_names",
+                return_value=mock_existing_tables,
+            ),
+        ):
+            call_command(
+                "migrate", "unmigrated_app_syncdb", run_syncdb=True, stdout=stdout
+            )
+            create_table_calls = [
+                str(call).upper()
+                for call in execute.mock_calls
+                if "CREATE TABLE" in str(call)
+            ]
+            self.assertEqual(len(create_table_calls), 2)
             self.assertGreater(len(execute.mock_calls), 2)
+            self.assertFalse(
+                any([table_name.upper() in call for call in create_table_calls])
+            )
             self.assertIn(
                 "Synchronize unmigrated app: unmigrated_app_syncdb", stdout.getvalue()
             )