Fixed #29808 -- Fixed initial migration detection when identifiers are case-insensitive.

Thanks Simon Charette for the review.
This commit is contained in:
Hasan Ramezani 2019-11-14 23:18:32 +01:00 committed by Mariusz Felisiak
parent d0c86a1df4
commit 530dd193f2
6 changed files with 95 additions and 4 deletions

View File

@ -329,8 +329,11 @@ class MigrationExecutor:
apps = after_state.apps apps = after_state.apps
found_create_model_migration = False found_create_model_migration = False
found_add_field_migration = False found_add_field_migration = False
fold_identifier_case = self.connection.features.ignores_table_name_case
with self.connection.cursor() as cursor: with self.connection.cursor() as cursor:
existing_table_names = set(self.connection.introspection.table_names(cursor)) existing_table_names = set(self.connection.introspection.table_names(cursor))
if fold_identifier_case:
existing_table_names = {name.casefold() for name in existing_table_names}
# Make sure all create model and add field operations are done # Make sure all create model and add field operations are done
for operation in migration.operations: for operation in migration.operations:
if isinstance(operation, migrations.CreateModel): if isinstance(operation, migrations.CreateModel):
@ -341,7 +344,10 @@ class MigrationExecutor:
model = global_apps.get_model(model._meta.swapped) model = global_apps.get_model(model._meta.swapped)
if should_skip_detecting_model(migration, model): if should_skip_detecting_model(migration, model):
continue continue
if model._meta.db_table not in existing_table_names: db_table = model._meta.db_table
if fold_identifier_case:
db_table = db_table.casefold()
if db_table not in existing_table_names:
return False, project_state return False, project_state
found_create_model_migration = True found_create_model_migration = True
elif isinstance(operation, migrations.AddField): elif isinstance(operation, migrations.AddField):
@ -358,7 +364,10 @@ class MigrationExecutor:
# Handle implicit many-to-many tables created by AddField. # Handle implicit many-to-many tables created by AddField.
if field.many_to_many: if field.many_to_many:
if field.remote_field.through._meta.db_table not in existing_table_names: through_db_table = field.remote_field.through._meta.db_table
if fold_identifier_case:
through_db_table = through_db_table.casefold()
if through_db_table not in existing_table_names:
return False, project_state return False, project_state
else: else:
found_add_field_migration = True found_add_field_migration = True
@ -368,7 +377,12 @@ class MigrationExecutor:
table, table,
) )
for column in columns: for column in columns:
if column.name == field.column: field_column = field.column
column_name = column.name
if fold_identifier_case:
column_name = column_name.casefold()
field_column = field_column.casefold()
if column_name == field_column:
found_add_field_migration = True found_add_field_migration = True
break break
else: else:

View File

@ -14,7 +14,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.backends.utils import truncate_name from django.db.backends.utils import truncate_name
from django.db.migrations.exceptions import InconsistentMigrationHistory from django.db.migrations.exceptions import InconsistentMigrationHistory
from django.db.migrations.recorder import MigrationRecorder from django.db.migrations.recorder import MigrationRecorder
from django.test import TestCase, override_settings from django.test import TestCase, override_settings, skipUnlessDBFeature
from .models import UnicodeModel, UnserializableModel from .models import UnicodeModel, UnserializableModel
from .routers import TestRouter from .routers import TestRouter
@ -197,6 +197,32 @@ class MigrateTests(MigrationTestBase):
self.assertTableNotExists("migrations_tribble", using=db) self.assertTableNotExists("migrations_tribble", using=db)
self.assertTableNotExists("migrations_book", using=db) self.assertTableNotExists("migrations_book", using=db)
@skipUnlessDBFeature('ignores_table_name_case')
def test_migrate_fake_initial_case_insensitive(self):
with override_settings(MIGRATION_MODULES={
'migrations': 'migrations.test_fake_initial_case_insensitive.initial',
}):
call_command('migrate', 'migrations', '0001', verbosity=0)
call_command('migrate', 'migrations', 'zero', fake=True, verbosity=0)
with override_settings(MIGRATION_MODULES={
'migrations': 'migrations.test_fake_initial_case_insensitive.fake_initial',
}):
out = io.StringIO()
call_command(
'migrate',
'migrations',
'0001',
fake_initial=True,
stdout=out,
verbosity=1,
no_color=True,
)
self.assertIn(
'migrations.0001_initial... faked',
out.getvalue().lower(),
)
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_fake_split_initial"}) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_fake_split_initial"})
def test_migrate_fake_split_initial(self): def test_migrate_fake_split_initial(self):
""" """

View File

@ -0,0 +1,28 @@
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
operations = [
migrations.CreateModel(
'fakeinitialmodel',
[
('id', models.AutoField(primary_key=True)),
('field', models.CharField(max_length=20)),
],
options={
'db_table': 'migrations_mIxEd_cAsE_iNiTiAl_mOdEl',
},
),
migrations.AddField(
model_name='fakeinitialmodel',
name='field_mixed_case',
field=models.CharField(max_length=20, db_column='fIeLd_mIxEd_cAsE'),
),
migrations.AddField(
model_name='fakeinitialmodel',
name='fake_initial_model',
field=models.ManyToManyField(to='migrations.fakeinitialmodel', db_table='m2m_mIxEd_cAsE'),
),
]

View File

@ -0,0 +1,23 @@
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
operations = [
migrations.CreateModel(
name='fakeinitialmodel',
fields=[
('id', models.AutoField(primary_key=True)),
('field', models.CharField(max_length=20)),
('field_mixed_case', models.CharField(max_length=20, db_column='FiEld_MiXeD_CaSe')),
(
'fake_initial_mode',
models.ManyToManyField('migrations.FakeInitialModel', db_table='m2m_MiXeD_CaSe'),
),
],
options={
'db_table': 'migrations_MiXeD_CaSe_InItIaL_MoDel',
},
),
]