#33583: sqlmigrate crashes on replaces to nonexistent migration.
-------------------------------------+-------------------------------------
Reporter: Amin Shah Gilani | Owner: nobody
Type: Bug | Status: closed
Component: Migrations | Version: 3.2
Severity: Normal | Resolution: invalid
Keywords: sqlmigrate, | Triage Stage:
migrate, squash | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Old description:
> Hello,
>
> This is my first bug report here so please ask for any additional details
> you need. I would also appreciate a workaround. If it is obvious, it
> escapes me.
>
> == Summary
>
> When an app is renamed with its migrations including a `replaces`
> reference to the old app and migration name (such as by squashing
> migrations), the behavior between `migrate` and `sqlmigrate` becomes
> inconsistent. `migrate` happily applies all migrations, but `sqlmigrate`
> reports an error.
>
> == Other information:
>
> * This bug was first observed in v3.2, but my demo shows it also affects
> v4.0.3.
> * This bug affects installations using the [https://github.com/python-
> social-auth/social-app-django social-app-django] package, which renamed
> its app multiple times,
> * I have a demo available [https://github.com/amingilani/django-bug-PoC
> here].
>
> == Steps to reproduce:
>
> **Step 1:** Create a migration file, in an app and then rename the app
> and migration. This will result in a migration similar to this:
>
> {{{#!python
>
> # alpha/migrations/0001_initial.py
> class Migration(migrations.Migration):
>
> replaces = [
> ('first', '0001_initial'),
> ]
>
> }}}
>
> **Step 2:** Ensure you have migrations in another app, with the first
> migration the second app referencing the old migration:
>
> {{{#!python
>
> class Migration(migrations.Migration):
>
> dependencies = [
> ('first', '0001_initial'),
> ]
>
> }}}
>
> **Step 3:** Note that `migrate` will happily apply all migrations:
>
> {{{#!shell
> python3 manage.py migrate
> Operations to perform:
> Apply all migrations: admin, alpha, auth, beta, contenttypes, sessions
> Running migrations:
> Applying contenttypes.0001_initial... OK
> Applying auth.0001_initial... OK
> Applying admin.0001_initial... OK
> Applying admin.0002_logentry_remove_auto_add... OK
> Applying admin.0003_logentry_add_action_flag_choices... OK
> Applying alpha.0001_initial... OK
> Applying contenttypes.0002_remove_content_type_name... OK
> Applying auth.0002_alter_permission_name_max_length... OK
> Applying auth.0003_alter_user_email_max_length... OK
> Applying auth.0004_alter_user_username_opts... OK
> Applying auth.0005_alter_user_last_login_null... OK
> Applying auth.0006_require_contenttypes_0002... OK
> Applying auth.0007_alter_validators_add_error_messages... OK
> Applying auth.0008_alter_user_username_max_length... OK
> Applying auth.0009_alter_user_last_name_max_length... OK
> Applying auth.0010_alter_group_name_max_length... OK
> Applying auth.0011_update_proxy_permissions... OK
> Applying auth.0012_alter_user_first_name_max_length... OK
> Applying beta.0001_initial... OK
> Applying beta.0002_auto_20220318_0245... OK
> Applying beta.0003_auto_20220318_0247... OK
> Applying sessions.0001_initial... OK
> }}}
>
> **Step 4:** However, `sqlmigrate` will raise an error:
>
> {{{#!shell
> python3 manage.py sqlmigrate beta 0003
> Traceback (most recent call last):
> File "/Users/amin/sandbox/mysite/manage.py", line 22, in <module>
> main()
> File "/Users/amin/sandbox/mysite/manage.py", line 18, in main
> execute_from_command_line(sys.argv)
> File "/usr/local/lib/python3.9/site-
> packages/django/core/management/__init__.py", line 446, in
> execute_from_command_line
> utility.execute()
> File "/usr/local/lib/python3.9/site-
> packages/django/core/management/__init__.py", line 440, in execute
> self.fetch_command(subcommand).run_from_argv(self.argv)
> File "/usr/local/lib/python3.9/site-
> packages/django/core/management/base.py", line 414, in run_from_argv
> self.execute(*args, **cmd_options)
> File "/usr/local/lib/python3.9/site-
> packages/django/core/management/commands/sqlmigrate.py", line 38, in
> execute
> return super().execute(*args, **options)
> File "/usr/local/lib/python3.9/site-
> packages/django/core/management/base.py", line 460, in execute
> output = self.handle(*args, **options)
> File "/usr/local/lib/python3.9/site-
> packages/django/core/management/commands/sqlmigrate.py", line 46, in
> handle
> loader = MigrationLoader(connection, replace_migrations=False)
> File "/usr/local/lib/python3.9/site-
> packages/django/db/migrations/loader.py", line 58, in __init__
> self.build_graph()
> File "/usr/local/lib/python3.9/site-
> packages/django/db/migrations/loader.py", line 276, in build_graph
> self.graph.validate_consistency()
> File "/usr/local/lib/python3.9/site-
> packages/django/db/migrations/graph.py", line 198, in
> validate_consistency
> [n.raise_error() for n in self.node_map.values() if isinstance(n,
> DummyNode)]
> File "/usr/local/lib/python3.9/site-
> packages/django/db/migrations/graph.py", line 198, in <listcomp>
> [n.raise_error() for n in self.node_map.values() if isinstance(n,
> DummyNode)]
> File "/usr/local/lib/python3.9/site-
> packages/django/db/migrations/graph.py", line 60, in raise_error
> raise NodeNotFoundError(self.error_message, self.key,
> origin=self.origin)
> django.db.migrations.exceptions.NodeNotFoundError: Migration
> beta.0001_initial dependencies reference nonexistent parent node
> ('first', '0001_initial')
> }}}
>
> **Note:** I have a demo available to try [https://github.com/amingilani
> /django-bug-PoC here].
New description:
Hello,
This is my first bug report here so please ask for any additional details
you need. I would also appreciate a workaround. If it is obvious, it
escapes me.
== Summary
When an app is renamed with its migrations including a `replaces`
reference to the old app and migration name (such as by squashing
migrations), the behavior between `migrate` and `sqlmigrate` becomes
inconsistent. `migrate` happily applies all migrations, but `sqlmigrate`
reports an error.
== Other information:
* This bug was first observed in v3.2, but my demo shows it also affects
v4.0.3.
* This bug affects installations using the [https://github.com/python-
social-auth/social-app-django social-app-django] package, which renamed
its app multiple times,
* I have a demo available [https://github.com/amingilani/django-bug-PoC
here].
== Steps to reproduce:
**Step 1:** Create a migration file, in an app and then rename the app and
migration. This will result in a migration similar to this:
{{{#!python
# alpha/migrations/0001_initial.py
class Migration(migrations.Migration):
replaces = [
('first', '0001_initial'),
]
}}}
**Step 2:** Ensure you have migrations in another app, with the first
migration the second app referencing the old migration:
{{{#!python
class Migration(migrations.Migration):
dependencies = [
('first', '0001_initial'),
]
}}}
**Step 3:** Note that `migrate` will happily apply all migrations:
{{{#!shell
python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, alpha, auth, beta, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying alpha.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying beta.0001_initial... OK
Applying beta.0002_auto_20220318_0245... OK
Applying beta.0003_auto_20220318_0247... OK
Applying sessions.0001_initial... OK
}}}
**Step 4:** However, `sqlmigrate` will raise an error:
{{{#!shell
python3 manage.py sqlmigrate beta 0003
Traceback (most recent call last):
File "/Users/amin/sandbox/mysite/manage.py", line 22, in <module>
main()
File "/Users/amin/sandbox/mysite/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.9/site-
packages/django/core/management/__init__.py", line 446, in
execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.9/site-
packages/django/core/management/__init__.py", line 440, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.9/site-
packages/django/core/management/base.py", line 414, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.9/site-
packages/django/core/management/commands/sqlmigrate.py", line 38, in
execute
return super().execute(*args, **options)
File "/usr/local/lib/python3.9/site-
packages/django/core/management/base.py", line 460, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.9/site-
packages/django/core/management/commands/sqlmigrate.py", line 46, in
handle
loader = MigrationLoader(connection, replace_migrations=False)
File "/usr/local/lib/python3.9/site-
packages/django/db/migrations/loader.py", line 58, in __init__
self.build_graph()
File "/usr/local/lib/python3.9/site-
packages/django/db/migrations/loader.py", line 276, in build_graph
self.graph.validate_consistency()
File "/usr/local/lib/python3.9/site-
packages/django/db/migrations/graph.py", line 198, in validate_consistency
[n.raise_error() for n in self.node_map.values() if isinstance(n,
DummyNode)]
File "/usr/local/lib/python3.9/site-
packages/django/db/migrations/graph.py", line 198, in <listcomp>
[n.raise_error() for n in self.node_map.values() if isinstance(n,
DummyNode)]
File "/usr/local/lib/python3.9/site-
packages/django/db/migrations/graph.py", line 60, in raise_error
raise NodeNotFoundError(self.error_message, self.key,
origin=self.origin)
django.db.migrations.exceptions.NodeNotFoundError: Migration
beta.0001_initial dependencies reference nonexistent parent node ('first',
'0001_initial')
}}}
**Note:** I have a demo available to try [https://github.com/amingilani
/django-bug-PoC here].
== Actual Behavior
`sqlmirate` raises an error but `migrate` processes the migrations without
a problem
== Expected Behavior
Both commands would consistently either raise an error on these
migrations, or run without raising an error
--
Comment (by Amin Shah Gilani):
Replying to [comment:1 Mariusz Felisiak]:
> Thanks for the report, this behavior was changed in
d88365708c554efe3c786c3be6da1d9de916360f. However, `replaces` is
documented and supported only for
[https://docs.djangoproject.com/en/stable/topics/migrations/#squashing-
migrations squashing migrations] so described flow was never officially
supported.
Thank you for your response. Would it then be correct to say that the
`migrate` command needs to be updated as well to raise an error? As it
stands the behavior is inconsistent. I've added existing and expected
behavior to the original ticket in order to clarify this problem.
Additionally, for applications that depend on relocated migrations in
third-party packages, will it be fine to update the now outdated pointer
to the new location of the migration?
In the context of the example above, is this fine?
{{{#!diff
-('first', '0001_initial'),
+('alpha, '0001_initial'),
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/33583#comment:2>
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 view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107017f9d8c41c8-36050d91-4a35-486c-a864-51ffeddc4578-000000%40eu-central-1.amazonses.com.