Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
ivancvetkovic-z opened this issue Jun 27, 2024 · 1 comment

Comments

@ivancvetkovic-z
Copy link

Software versions

  • Django: 5.0.6
  • mssql-django: 1.5
  • python: 3.10.12
  • SQL Server: 16.0.1000.6
  • OS: Ubuntu 22.04.4 LTS

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
Copy link
Contributor

mShan0 commented Jun 28, 2024

Thanks for the detailed report. Will look into this further

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants