#24123: LookupError when rolling back migrations
---------------------------------+--------------------------------------
     Reporter:  MarkusH          |                    Owner:  MarkusH
         Type:  Bug              |                   Status:  new
    Component:  Migrations       |                  Version:  master
     Severity:  Release blocker  |               Resolution:
     Keywords:                   |             Triage Stage:  Unreviewed
    Has patch:  0                |      Needs documentation:  0
  Needs tests:  0                |  Patch needs improvement:  0
Easy pickings:  0                |                    UI/UX:  0
---------------------------------+--------------------------------------
Changes (by MarkusH):

 * needs_docs:   => 0
 * needs_tests:   => 0
 * needs_better_patch:   => 0


Old description:

> 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 `LookupError`s due to missing models:
>
> {{{#!python
> 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
>
> {{{#!python
> [
>     (<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`.

New description:

 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 `LookupError`s due to missing models:

 {{{#!python
 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`.

--

Comment:

 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

 {{{#!console
 $ 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...
 }}}
 {{{#!python
 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:
 }}}

--
Ticket URL: <https://code.djangoproject.com/ticket/24123#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/065.0b72b73195c19359e942636a4f87e0cf%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to