Opened 10 years ago
Closed 4 years ago
#24282 closed Bug (fixed)
Cannot Modify Foreign Keys Within Data Migrations
Reported by: | Jeff Singer | Owned by: | Markus Holtermann |
---|---|---|---|
Component: | Migrations | Version: | 1.8alpha1 |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
The following data migration works in 1.7, but fails in 1.8a1
def forwards(apps, schema_editor): A = apps.get_model('app_name', 'A') B = apps.get_model('app_name', 'B') a = A.objects.create() b = B(a=a)
Fails with:
ValueError: Cannot assign "<A: A object>": "B.a" must be a "A" instance.
I believe that this is because the model classes returned apps.get_model
aren't necessarily instances of the right type.
I'd be happy to create a failing test case, however, I'm new to the project, and would like a little direction on where that should go.
Attachments (2)
Change History (18)
comment:1 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
I'll create patch for this
by , 10 years ago
Attachment: | ticket_24282.patch added |
---|
test to reproduce the issue (but it is passing correct)
comment:3 by , 10 years ago
Can not reproduce.
Latest commit on 1.8.x branch locally was
https://github.com/django/django/commit/fc1e9107d7d750f6eed3c8e679dfe96af8f05150
I created test project and similar datamigration runs correct.
I also created a test for it (see prev. comment), and it is passing.
Maybe, I'm missing something?
comment:4 by , 10 years ago
I also couldn't reproduce using the models from the tutorial. Jeff, could you provide more details?
def forwards(apps, schema_editor): Poll = apps.get_model('polls', 'Poll') Choice = apps.get_model('polls', 'Choice') a = Poll.objects.create(question='foo bar', pub_date=timezone.now()) b = Choice(poll=a)
comment:5 by , 10 years ago
Unfortunately, I can't share the exact setup, because it's a project from work.
The main difference I can see is between your example and my actual situation, is that the models are from different apps.
I should have some time tomorrow night to try to create a test case for this.
comment:6 by , 10 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:7 by , 10 years ago
Jeff, could you test with the patch from PR 4097 and see if that solves this issue?
comment:8 by , 10 years ago
Still fails unfortunately when tested against that patch.
I am having trouble creating a failing test case though. The PR definitely seems related.
Here's a few things I've noticed since today:
- It's not actually a ForeignKey, but the forward end of a OneToOneField. I'm doubting that matters, but I figured I'd throw that out there.
- The repr of type(a) and field.rel.to are the same, however, the id of them is not the same.
- The id of type(a) is the same as the one in the apps.all_models dictionary.
- Stepping through several migrations of the same app, the id(type(a)) is different each time, which makes sense, as the model is changing. However, id(field.rel.to) does not match the current version of a, but one from a previous migration.
Hope that helps a little bit, in the mean time, I'll keep trying to get a testcase that reproduces this.
comment:9 by , 10 years ago
I've created a test that fails.
It creates two models, in two seperate apps, and then alters the one with a ForeignKey pointing at it before running the DataMigration.
def inner_method(models, schema_editor): Author = models.get_model("test_authors", "Author") Book = models.get_model("test_books", "Book") author = Author.objects.create(name="Hemingway") book = Book("Old Man and The Sea") book.author = author create_author = migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ], options={}, ) create_book = migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("title", models.CharField(max_length=100)), ("author", models.ForeignKey("test_authors.Author")) ], options={}, ) add_hometown = migrations.AddField( "Author", "hometown", models.CharField(max_length=100), ) create_old_man = migrations.RunPython( inner_method, inner_method ) project_state = ProjectState() with connection.schema_editor() as editor: new_state = project_state.clone() create_author.state_forwards("test_authors", new_state) create_author.database_forwards("test_authors", editor, project_state, new_state) project_state, new_state = new_state, new_state.clone() create_book.state_forwards("test_books", new_state) create_book.database_forwards("test_books", editor, project_state, new_state) project_state, new_state = new_state, new_state.clone() add_hometown.state_forwards("test_authors", new_state) add_hometown.database_forwards("test_authors", editor, project_state, new_state) project_state, new_state = new_state, new_state.clone() create_old_man.state_forwards("test_books", new_state) create_old_man.database_forwards("test_books", editor, project_state, new_state)
comment:10 by , 10 years ago
Has patch: | set |
---|---|
Owner: | set to |
Status: | new → assigned |
Thanks Jeff. I added your test case to the pull request. Before applying the patch (4th commit in the PR right now), your tests fails, after my patch it succeeds. Waiting for CI confirmation right now.
comment:12 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:15 by , 4 years ago
Resolution: | fixed |
---|---|
Status: | closed → new |
I am getting this same issue in Django==2.2.10 as well as 2.2.17
When using models obtained from apps.get_model
they are returned as <class 'fake.ModelName'> and if I try to assign it to a ForeignKey field that causes a ValueError Exception:
ValueError: Cannot assign "<ModelName: ModelName object (10232)>": "ModelName.field_name" must be a "ModelName" instance.
This can be avoided by using PKs instead, but I am not sure if it's supposed to work with objects as well.
instance.foreign_key_field_id = instance.pk
comment:16 by , 4 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
@Ubica, please don’t re-open old issues.
If you believe there is a bug, please open a new issue, with a complete description, that will enable us to reproduce and assess the report.
Please note that Django v2.2 no longer receives bug fixes, as it is in extended support, and so ideally your report will reproduce against Django’s master branch. Thanks.
This is surely a consequence of the migration optimizations introduced in 1.8. One possible resolution would be to ditch the
isinstance
check and replace it by asame_model
comparison utility which would compare models using their_meta
app_label/model_name combination.