3YOURMIND / django-migration-linter

:rocket: Detect backward incompatible migrations for your django project
https://pypi.python.org/pypi/django-migration-linter/
Apache License 2.0
530 stars 58 forks source link

Handle removing unique_together #289

Closed moritz89 closed 7 months ago

moritz89 commented 7 months ago

This has been mentioned in #66 but seems to be present, at least in version 5.1.0. Linting a migration that drops the unique_together constraint will result in the following error and then abort the linting process. The error occurred in django-address, but also in our app's migrations.

WARNING | main | django_migration_linter.get_sql:337 | Error while executing sqlmigrate on (address, 0002_auto_20160213_1726) with exception: Found wrong number (0) of constraints for address_locality(name, state_id).
Traceback (most recent call last):
  File "/HOME/app/./manage.py", line 15, in <module>
    execute_from_command_line(sys.argv)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/HOME/virtualenv/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/virtualenv/lib/python3.10/site-packages/django/core/management/base.py", line 413, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django_migration_linter/management/commands/lintmigrations.py", line 180, in handle
    linter.lint_all_migrations(
  File "/HOME/virtualenv/lib/python3.10/site-packages/django_migration_linter/migration_linter.py", line 151, in lint_all_migrations
    self.lint_migration(m)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django_migration_linter/migration_linter.py", line 175, in lint_migration
    sql_statements = self.get_sql(app_label, migration_name)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django_migration_linter/migration_linter.py", line 319, in get_sql
    sql_statement = call_command(
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/core/management/__init__.py", line 194, in call_command
    return command.execute(*args, **defaults)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/core/management/commands/sqlmigrate.py", line 38, in execute
    return super().execute(*args, **options)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/core/management/commands/sqlmigrate.py", line 80, in handle
    sql_statements = loader.collect_sql(plan)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/db/migrations/loader.py", line 381, in collect_sql
    state = migration.apply(state, schema_editor, collect_sql=True)
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/db/migrations/migration.py", line 132, in apply
    operation.database_forwards(
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/db/migrations/operations/models.py", line 659, in database_forwards
    alter_together(
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/db/backends/base/schema.py", line 594, in alter_unique_together
    self._delete_composed_index(
  File "/HOME/virtualenv/lib/python3.10/site-packages/django/db/backends/base/schema.py", line 650, in _delete_composed_index
    raise ValueError(
ValueError: Found wrong number (0) of constraints for address_locality(name, state_id)

A work-around is to run the linter with --ignore-sqlmigrate-errors but I guess this might ignore other things as well. Is this the intended behavior?

David-Wobrock commented 7 months ago

Hi,

Thanks for taking interest in the project!

That's linked to the way {unique,index}_together is implemented on Django side. Those constraints need to query the Database to retrieve the actual index name when deleting them, so that they can build the corresponding SQL query.

So when we are generating the SQL for a migration deleting such a constraint, it might be that the constraint has been deleted long ago (or that it has not been created yet, if the DB is created from scratch).

For now, the best option is indeed to ignore sqlmigrate errors, or to ignore this one migration specifically.

I hope this helps :)