Opened 5 years ago

Closed 5 years ago

#24123 closed Bug (fixed)

LookupError when rolling back migrations

Reported by: Markus Holtermann Owned by: Markus Holtermann
Component: Migrations Version: master
Severity: Release blocker Keywords:
Cc: cmawebsite@… Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Markus Holtermann)

The MigrationExecutor.migrate() method generates the initial project state from the first migration that is being applied/unapplied. If this migration has no relations to other apps that are also being (un)applied, this can lead to LookupErrors due to missing models:

Traceback (most recent call last):
  File "/home/markus/Coding/django/django/apps/registry.py", line 148, in get_app_config
    return self.app_configs[app_label]
KeyError: 'app_b'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/markus/Coding/django/django/db/migrations/state.py", line 181, in __init__
    model = self.get_model(lookup_model[0], lookup_model[1])
  File "/home/markus/Coding/django/django/apps/registry.py", line 202, in get_model
    return self.get_app_config(app_label).get_model(model_name.lower())
  File "/home/markus/Coding/django/django/apps/registry.py", line 150, in get_app_config
    raise LookupError("No installed app with label '%s'." % app_label)
LookupError: No installed app with label 'app_b'.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/markus/Coding/django/django/core/management/__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "/home/markus/Coding/django/django/core/management/__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/markus/Coding/django/django/core/management/base.py", line 390, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/markus/Coding/django/django/core/management/base.py", line 444, in execute
    output = self.handle(*args, **options)
  File "/home/markus/Coding/django/django/core/management/commands/migrate.py", line 213, in handle
    executor.migrate(targets, plan, fake=options.get("fake", False))
  File "/home/markus/Coding/django/django/db/migrations/executor.py", line 75, in migrate
    state = self.unapply_migration(state, migration, fake=fake)
  File "/home/markus/Coding/django/django/db/migrations/executor.py", line 130, in unapply_migration
    state = migration.unapply(state, schema_editor)
  File "/home/markus/Coding/django/django/db/migrations/migration.py", line 157, in unapply
    operation.database_backwards(self.app_label, schema_editor, from_state, to_state)
  File "/home/markus/Coding/django/django/db/migrations/operations/models.py", line 62, in database_backwards
    model = from_state.apps.get_model(app_label, self.name)
  File "/home/markus/Coding/django/django/utils/functional.py", line 60, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/markus/Coding/django/django/db/migrations/state.py", line 82, in apps
    return StateApps(self.real_apps, self.models)
  File "/home/markus/Coding/django/django/db/migrations/state.py", line 191, in __init__
    raise ValueError(msg.format(field=operations[0][1], model=lookup_model))
ValueError: Lookup failed for model referenced by field app_a.A3.b2: app_b.B2

Given the attached migration dependencies, the backwards plan for ('app_a', None) is

[
    (<Migration app_c.0003_c3>, True),
    (<Migration app_a.0004_a4>, True),
    (<Migration app_a.0003_a3>, True),
    (<Migration app_c.0002_c2>, True),
    (<Migration app_b.0003_b3>, True),
    (<Migration app_b.0002_b2>, True),
    (<Migration app_a.0002_a2>, True),
    (<Migration app_a.0001_initial>, True)
]

The initial state generated from app_c.0003_c3 is

  app_a.a1:
  app_a.a2:
  app_c.c1:
  app_c.c2:

As one can see, there are no models of app_b, which eventually leads to an error when unapplying app_1.0003_a3 which tries to look up model app_b.B2.

Attachments (1)

LookupError.png (14.4 KB) - added by Markus Holtermann 5 years ago.

Download all attachments as: .zip

Change History (6)

Changed 5 years ago by Markus Holtermann

Attachment: LookupError.png added

comment:1 Changed 5 years ago by Markus Holtermann

Description: modified (diff)

I created a sample project to replicate the error: https://github.com/MarkusH/django-ticket-triage/tree/bug24123

To replicate the above error for forwards migrations, take a clean database and run

$ python manage.py migrate app_b
Operations to perform:
  Apply all migrations: app_b
Running migrations:
  Applying app_a.0001_initial... OK
  Applying app_a.0002_a2... OK
  Applying app_b.0002_b2... OK
  Applying app_b.0003_b3... OK
$ python manage.py migrate
Operations to perform:
  Apply all migrations: app_a, app_c, app_b
Running migrations:
  Applying app_c.0002_c2... OK
  Applying app_a.0003_a3...
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/markus/Coding/django/django/core/management/__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "/home/markus/Coding/django/django/core/management/__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/markus/Coding/django/django/core/management/base.py", line 390, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/markus/Coding/django/django/core/management/base.py", line 444, in execute
    output = self.handle(*args, **options)
  File "/home/markus/Coding/django/django/core/management/commands/migrate.py", line 213, in handle
    executor.migrate(targets, plan, fake=options.get("fake", False))
  File "/home/markus/Coding/django/django/db/migrations/executor.py", line 73, in migrate
    state = self.apply_migration(state, migration, fake=fake)
  File "/home/markus/Coding/django/django/db/migrations/executor.py", line 110, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/home/markus/Coding/django/django/db/migrations/migration.py", line 110, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "/home/markus/Coding/django/django/db/migrations/operations/models.py", line 59, in database_forwards
    schema_editor.create_model(model)
  File "/home/markus/Coding/django/django/db/backends/schema.py", line 233, in create_model
    definition, extra_params = self.column_sql(model, field)
  File "/home/markus/Coding/django/django/db/backends/schema.py", line 132, in column_sql
    db_params = field.db_parameters(connection=self.connection)
  File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1996, in db_parameters
    return {"type": self.db_type(connection), "check": []}
  File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1987, in db_type
    rel_field = self.related_field
  File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1893, in related_field
    return self.foreign_related_fields[0]
  File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1627, in foreign_related_fields
    return tuple(rhs_field for lhs_field, rhs_field in self.related_fields)
  File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1614, in related_fields
    self._related_fields = self.resolve_related_fields()
  File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1599, in resolve_related_fields
    raise ValueError('Related model %r cannot be resolved' % self.rel.to)
ValueError: Related model 'app_b.B2' cannot be resolved

The plan for the second migrate command is

[
    (<Migration app_c.0002_c2>, False),
    (<Migration app_a.0003_a3>, False),
    (<Migration app_a.0004_a4>, False),
    (<Migration app_c.0003_c3>, False)
]

The initial state is:

  app_a.a1:
  app_a.a2:
  app_c.c1:

comment:2 Changed 5 years ago by Collin Anderson

Cc: cmawebsite@… added
Triage Stage: UnreviewedAccepted

comment:3 Changed 5 years ago by Markus Holtermann

Has patch: set

comment:4 Changed 5 years ago by Tim Graham

Triage Stage: AcceptedReady for checkin

comment:5 Changed 5 years ago by Markus Holtermann <info@…>

Resolution: fixed
Status: newclosed

In bbbed99f6260a8b3e65cb990e49721b1ce4a441b:

Fixed #24123 -- Used all available migrations to generate the initial migration state

Thanks Collin Anderson for the input when creating the patch and Tim Graham for the review.

Note: See TracTickets for help on using tickets.
Back to Top