Opened 7 years ago
Last modified 4 years ago
#29177 new Bug
Unmanaged models with ForeignKeys do not get those fields serialized into their migration state when CreateModel happens. — at Initial Version
Reported by: | Keryn Knight | Owned by: | nobody |
---|---|---|---|
Component: | Migrations | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Keryn Knight, Daniel Naab, Fabio Sangiovanni | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | yes |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
Given models like the following, where B
is unmanaged, and C
is a normal concrete Model
:
from django.db import models class A(models.Model): class Meta: db_table = "test_a" class B(models.Model): a = models.ForeignKey(A, related_name="+", on_delete=models.CASCADE) class Meta: managed = False db_table = "test_b" class C(models.Model): a = models.ForeignKey(A, related_name="+", on_delete=models.CASCADE) class Meta: db_table = "test_c"
when python manage.py makemigrations <appname>
is called, B
ends up looking like:
migrations.CreateModel( name='B', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ], options={ 'db_table': 'test_b', 'managed': False, }, )
whilst C
correctly gets:
migrations.CreateModel( name='C', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='whee.A')), ], options={ 'db_table': 'test_c', }, )
Because B
doesn't have the a
attribute, any subsequent data migrations which would like to query on it, like so:
def forwards(apps, schema_editor): B = apps.get_model('whee', 'B') B.objects.filter(a=1)
will crash.
With the assistance of Markus on IRC:
MarkusH: do you want to try what's gonna happen when you just drop the if-condition in https://github.com/django/django/blob/75527c0f8360ae3555fcf67ce19b4cb910b69b9d/django/db/migrations/autodetector.py#L570-L572 ?
commenting out the lines
if not model_opts.managed: continue
allows the autodetector to generate the following:
migrations.CreateModel( name='A', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ], options={ 'db_table': 'test_a', }, ), migrations.AddField( model_name='b', name='a', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='whee.A'), )
and subsequently the data migration can access B.a
If A
itself is managed=False
the migration generated is instead:
migrations.CreateModel( name='B', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='whee.A')), ], options={ 'db_table': 'test_b', 'managed': False, }, )
Running the test suite via ./runtests.py
as of commit 75527c0f8360ae3555fcf67ce19b4cb910b69b9d doesn't seem to cause any failures, with those lines commented out.
Markus also pointed out commit https://github.com/django/django/commit/215aa4f53b6bbd07d5c1eecfa94e7fcd00da813e which references #23415
I noticed this "issue" while bringing a project up from 1.9, so it's not a recent problem.