mirror of https://github.com/django/django.git
Fixed #29808 -- Fixed initial migration detection when identifiers are case-insensitive.
Thanks Simon Charette for the review.
This commit is contained in:
parent
d0c86a1df4
commit
530dd193f2
|
@ -329,8 +329,11 @@ class MigrationExecutor:
|
|||
apps = after_state.apps
|
||||
found_create_model_migration = False
|
||||
found_add_field_migration = False
|
||||
fold_identifier_case = self.connection.features.ignores_table_name_case
|
||||
with self.connection.cursor() as 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
|
||||
for operation in migration.operations:
|
||||
if isinstance(operation, migrations.CreateModel):
|
||||
|
@ -341,7 +344,10 @@ class MigrationExecutor:
|
|||
model = global_apps.get_model(model._meta.swapped)
|
||||
if should_skip_detecting_model(migration, model):
|
||||
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
|
||||
found_create_model_migration = True
|
||||
elif isinstance(operation, migrations.AddField):
|
||||
|
@ -358,7 +364,10 @@ class MigrationExecutor:
|
|||
|
||||
# Handle implicit many-to-many tables created by AddField.
|
||||
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
|
||||
else:
|
||||
found_add_field_migration = True
|
||||
|
@ -368,7 +377,12 @@ class MigrationExecutor:
|
|||
table,
|
||||
)
|
||||
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
|
||||
break
|
||||
else:
|
||||
|
|
|
@ -14,7 +14,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
|||
from django.db.backends.utils import truncate_name
|
||||
from django.db.migrations.exceptions import InconsistentMigrationHistory
|
||||
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 .routers import TestRouter
|
||||
|
@ -197,6 +197,32 @@ class MigrateTests(MigrationTestBase):
|
|||
self.assertTableNotExists("migrations_tribble", 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"})
|
||||
def test_migrate_fake_split_initial(self):
|
||||
"""
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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',
|
||||
},
|
||||
),
|
||||
]
|
Loading…
Reference in New Issue