microsoft / mssql-django

The Microsoft Django backend for SQL Server provides a connectivity layer for Django on SQL Server or Azure SQL DB.
Other
323 stars 108 forks source link

Migration can fail if previous migration has an index with a field that starts with hyphen #405

Open ivancvetkovic-z opened 1 week ago

ivancvetkovic-z commented 1 week ago

Software versions

Table schema and Model

class Post(models.Model):
    title = models.CharField(max_length=200, null=True)
    date_posted = models.DateTimeField()

    class Meta:
        indexes = [models.Index(fields=['-date_posted'])]

Database Connection Settings

DATABASES = {
    "default": {
        "ENGINE": "mssql",
...
        "OPTIONS": {"driver": "ODBC Driver 18 for SQL Server", 'extra_params': "Encrypt=no"},
    },
}

Problem description and steps to reproduce Migration raises exception when it calls _delete_indexes in schema.py and the previous migration has an index with a field that starts with a hyphen (descending order).

Make a project with the model above and two migrations:

migrations/0001_initial.py

class Migration(migrations.Migration):
    initial = True
    dependencies = []

    operations = [
        migrations.CreateModel(
            name='Post',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=200)),
                ('date_posted', models.DateTimeField()),
            ],
            options={
                'indexes': [models.Index(fields=['-date_posted'], name='blogs_post_date_po_acca62_idx')],
            },
        ),
    ]

migrations/0002_alter_post_title.py

class Migration(migrations.Migration):
    dependencies = [('blogs', '0001_initial'),]

    operations = [
        migrations.AlterField(model_name='post', name='title', field=models.CharField(max_length=200, null=True)),
    ]

Run python manage.py migrate on an empty database.

Expected behavior and actual behavior The both migrations should succeed.

Instead, the migration 0002_alter_post_title breaks with the exception django.core.exceptions.FieldDoesNotExist: Post has no field named '-date_posted'

Error message/stack trace

Traceback (most recent call last):
  File "/home/user/PycharmProjects/idxtest/manage.py", line 22, in <module>
    main()
  File "/home/user/PycharmProjects/idxtest/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/core/management/base.py", line 413, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/core/management/base.py", line 107, in wrapper
    res = handle_func(*args, **kwargs)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 356, in handle
    post_migrate_state = executor.migrate(
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/migrations/executor.py", line 135, in migrate
    state = self._migrate_all_forwards(
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
    state = self.apply_migration(
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/migrations/migration.py", line 132, in apply
    operation.database_forwards(
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/migrations/operations/fields.py", line 235, in database_forwards
    schema_editor.alter_field(from_model, from_field, to_field)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/backends/base/schema.py", line 893, in alter_field
    self._alter_field(
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/mssql/schema.py", line 618, in _alter_field
    indexes_dropped = self._delete_indexes(model, old_field, new_field)
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/mssql/schema.py", line 926, in _delete_indexes
    columns = [model._meta.get_field(field).column for field in index.fields]
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/mssql/schema.py", line 926, in <listcomp>
    columns = [model._meta.get_field(field).column for field in index.fields]
  File "/home/user/.local/share/virtualenvs/idxtest-Z4Fcl3b8/lib/python3.10/site-packages/django/db/models/options.py", line 683, in get_field
    raise FieldDoesNotExist(
django.core.exceptions.FieldDoesNotExist: Post has no field named '-date_posted'

Any other details that can be helpful The problem is this line in _delete_indexes from schema.py

            columns = [model._meta.get_field(field).column for field in index.fields]

If the index.fields has a field in descending order, that field has a hyphen before the name and the get_field fails. The offending line should use index.fields_orders instead:

            columns = [model._meta.get_field(field).column for field, _ in index.fields_orders]
mShan0 commented 1 week ago

Thanks for the detailed report. Will look into this further