diff options
| author | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2026-03-08 10:44:56 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-08 10:44:56 +0100 |
| commit | a5c49174b96302f39682cce48f82da67b3b08351 (patch) | |
| tree | 00ccff1c2fdecdd0f536c04a9a89eb5fbe94e5ee | |
| parent | 0ae0029c2f4d82af937e9632cd5bf13e81e91f98 (diff) | |
Fixed #12529 -- Fixed migrate --run-syncdb crash for existing model with truncated db_table names.
| -rw-r--r-- | django/core/management/commands/migrate.py | 4 | ||||
| -rw-r--r-- | tests/migrations/migrations_test_apps/unmigrated_app_syncdb/models.py | 5 | ||||
| -rw-r--r-- | tests/migrations/test_commands.py | 53 |
3 files changed, 58 insertions, 4 deletions
diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py index 268f669ba2..62ad29e43d 100644 --- a/django/core/management/commands/migrate.py +++ b/django/core/management/commands/migrate.py @@ -6,6 +6,7 @@ from django.apps import apps 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 @@ class Command(BaseCommand): 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 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 9f3179cd0d..c539b20d11 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 6a0d9bd6d2..e9929c1eaf 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -25,6 +25,7 @@ from django.db import ( 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 @@ class MigrateTests(MigrationTestBase): 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 @@ class MigrateTests(MigrationTestBase): 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() ) |
