Opened 10 years ago
Last modified 10 years ago
#24572 new Bug
Migration plan may be broken when applying migrations on a clean database
Reported by: | Markus Holtermann | Owned by: | nobody |
---|---|---|---|
Component: | Migrations | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Florian Apolloner, Shai Berger | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
python manage.py startapp a
python manage.py startapp b
It is important that app a
is in alphabetically ordering before app b
- Add both apps to
INSTALLED_APPS
- Add a model to app
a
:from django.db import models class MyModel(models.Model): pass
- Add a model to app
b
:from django.db import models class MyModel(models.Model): pass
python manage.py makemigrations
- Add a
ForeignKey
fromb.MyModel
toa.MyModel
from django.db import models class MyModel(models.Model): a = models.ForeignKey('a.MyModel', null=True)
python manage.py makemigrations
- Remove the
ForeignKey
onb.MyModel
toa.MyModel
from django.db import models class MyModel(models.Model): pass
python manage.py makemigrations
- Remove
a.MyModel
python manage.py makemigrations
python manage.py showmigrations --plan -v 3
[ ] a.0001_initial [ ] a.0002_delete_mymodel ... (a.0001_initial) [ ] b.0001_initial [ ] b.0002_mymodel_a ... (a.0001_initial, b.0001_initial) [ ] b.0003_remove_mymodel_a ... (b.0002_mymodel_a)
$ python manage.py migrate
Operations to perform: Apply all migrations: a, b Running migrations: Rendering model states... DONE Applying a.0001_initial... OK Applying a.0002_delete_mymodel... OK Applying b.0001_initial... OK Applying b.0002_mymodel_a...
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 330, in execute_from_command_line utility.execute() File "/home/markus/Coding/django/django/core/management/__init__.py", line 322, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/home/markus/Coding/django/django/core/management/base.py", line 347, in run_from_argv self.execute(*args, **cmd_options) File "/home/markus/Coding/django/django/core/management/base.py", line 398, in execute output = self.handle(*args, **options) File "/home/markus/Coding/django/django/core/management/commands/migrate.py", line 195, in handle executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial) File "/home/markus/Coding/django/django/db/migrations/executor.py", line 110, in migrate self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial) File "/home/markus/Coding/django/django/db/migrations/executor.py", line 147, in apply_migration state = migration.apply(state, schema_editor) File "/home/markus/Coding/django/django/db/migrations/migration.py", line 118, in apply operation.database_forwards(self.app_label, schema_editor, old_state, project_state) File "/home/markus/Coding/django/django/db/migrations/operations/fields.py", line 62, in database_forwards field, File "/home/markus/Coding/django/django/db/backends/sqlite3/schema.py", line 220, in add_field self._remake_table(model, create_fields=[field]) File "/home/markus/Coding/django/django/db/backends/sqlite3/schema.py", line 179, in _remake_table self.create_model(temp_model) File "/home/markus/Coding/django/django/db/backends/base/schema.py", line 231, in create_model definition, extra_params = self.column_sql(model, field) File "/home/markus/Coding/django/django/db/backends/base/schema.py", line 130, in column_sql db_params = field.db_parameters(connection=self.connection) File "/home/markus/Coding/django/django/db/models/fields/related.py", line 2052, in db_parameters return {"type": self.db_type(connection), "check": []} File "/home/markus/Coding/django/django/db/models/fields/related.py", line 2043, in db_type rel_field = self.target_field File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1946, in target_field return self.foreign_related_fields[0] File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1704, 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 1691, in related_fields self._related_fields = self.resolve_related_fields() File "/home/markus/Coding/django/django/db/models/fields/related.py", line 1676, in resolve_related_fields raise ValueError('Related model %r cannot be resolved' % self.remote_field.model) ValueError: Related model 'a.MyModel' cannot be resolved
The problem is that the migration a.0002_delete_mymodel
doesn't have any information that it must be applied after b.0003_remove_mymodel_a
which in turn removes any references to a.MyModel
again.
Change History (5)
comment:1 by , 10 years ago
comment:2 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
There's more to this than just the fact that a.2 requires b.3 but b.3 is not a dependency of a.2. Even if a.2 exists but is not run, the above error will be thrown when migrating to b.2. Git bisect shows that this particular error is a regression from 1aa3e09c2043c88a760e8b73fb95dc8f1ffef50e.
Fixing the regressions from that commit at least opens up an easy workaround that doesn't involve deleting migration files. A simple manage.py migrate b
followed by manage.py migrate a
will work.
comment:3 by , 10 years ago
Actually it is bbbed99f6260a8b3e65cb990e49721b1ce4a441b. Which also makes way more sense, as this does use a.2
to generate the initial state right before each migration that needs to be applied. See https://github.com/django/django/blob/3e7d9d05ac5efff4e4732c3453c7a8ef502d0ed0/django/db/migrations/executor.py#L65 for details.
comment:4 by , 10 years ago
Cc: | added |
---|
comment:5 by , 10 years ago
Cc: | added |
---|
The right workaround is to add the dependency manually.
Adding the dependency automatically would require keeping close track of the dependency at the model and field level.
I created a demo project to demonstrate the problem: https://github.com/MarkusH/django-ticket-triage/compare/master...ticket24572