Opened 8 years ago
Closed 20 months ago
#28056 closed Bug (duplicate)
Reverse migration for model rename with cross-app ForeignKey fails
Reported by: | Paul Tiplady | Owned by: | Bhuvnesh |
---|---|---|---|
Component: | Migrations | Version: | 1.10 |
Severity: | Normal | Keywords: | |
Cc: | Friedrich Delgado | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
When attempting to reverse a migration for the rename of a model, where the model has ForeignKey references to it from models in other apps, the reverse migration fails with the following error:
ValueError: The field <foreignkey_field> was declared with a lazy reference to '<old_model_name>', but app 'one' doesn't provide model '<old_model_name>'
I've put a simple repro up on github here: https://github.com/paultiplady/django-migration-bug, but here's an outline of the problem:
With two apps, one
and two
, and the models:
one/models.py:
from django.db import models class Rename1(models.Model): pass
two/models.py:
from django.db import models class Related2(models.Model): rename1 = models.ForeignKey('one.Rename1')
Renaming Rename1 to Rename2 produces a simple migration:
0002_auto_20170408_1617.py:
class Migration(migrations.Migration): dependencies = [ ('one', '0001_initial'), ] operations = [ migrations.RenameModel( old_name='Rename1', new_name='Rename2', ), ]
But when this migration is reversed, the error is raised:
ValueError: The field two.Related2.rename1 was declared with a lazy reference to 'one.rename1', but app 'one' doesn't provide model 'rename1'.
It's unclear from the error message how I'd even go about attempting to address this problem, and the docs don't mention anything about handling this case (as far as I can tell).
Expected behaviour is to be able to handle this reverse-rename seamlessly, or at least provide a sensible error message if this operation is unsupported.
Naively it looks like the problem is that the two.Related2
model has not been given its own migration when its ForeignKey was changed from one.Rename1
to one.Rename2
, instead that change is tracked implicitly and incorrectly in the RenameModel step.
Change History (10)
comment:1 by , 8 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:2 by , 8 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:3 by , 8 years ago
Can't we have a different migration for rename1 as alterfield, so that its migration's state handled properly.
comment:6 by , 8 years ago
Patch needs improvement: | set |
---|
As described on the pull request, based on my testing, the patch doesn't solve the issue.
comment:7 by , 5 years ago
Cc: | added |
---|
comment:8 by , 2 years ago
I think this issue was solved due to aa4acc164d1247c0de515c959f7b09648b57dc42 and can be closed now.
comment:9 by , 20 months ago
Owner: | set to |
---|
comment:10 by , 20 months ago
There seems to be some problem with this ticket, I wasn't able to reproduce with django v1.10.7 as described above. I played with the sample repo provided and observed that the error :
ValueError: The field two.Related2.rename1 was declared with a lazy reference to 'one.rename1', but app 'one' doesn't provide model 'rename1'.
is produced when we run makemigrations command again after the RenameModel migration file is generated. I think at that time django didn't had the functionality to rename the cross-app referenced models, we can solve this by editing the migration file two.0001_initial and set the fk to "one.rename2" - removes the ValueError .
Another error:
ValueError: Related model 'one.Rename1' cannot be resolved
is thrown on migrating which is removed if we add a field instead of "pass" inside one.rename1
model.
comment:11 by , 20 months ago
Resolution: | → duplicate |
---|---|
Status: | assigned → closed |
Good catch! ('two', '0001_initial')
dependency was missing in migration generated in Django < 2.0,
Duplicate of #28493, fixed by 0891503fad458e7dfabc5adbf314f80e78e63f14.
I think the problem is in
ForeignKey.db_type()
. It usesself.target_field
which is using the state of the current model files rather than migration's state.