Opened 8 years ago
Last modified 5 months ago
#29177 assigned 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 | Triage Stage: | Accepted |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| 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.