#29177: Unmanaged models with ForeignKeys do not get those fields serialized 
into
their migration state when CreateModel happens.
------------------------------+--------------------------------------
     Reporter:  Keryn Knight  |                    Owner:  nobody
         Type:  Bug           |                   Status:  new
    Component:  Migrations    |                  Version:  master
     Severity:  Normal        |               Resolution:
     Keywords:                |             Triage Stage:  Unreviewed
    Has patch:  0             |      Needs documentation:  0
  Needs tests:  0             |  Patch needs improvement:  0
Easy pickings:  0             |                    UI/UX:  0
------------------------------+--------------------------------------
Description changed by Keryn Knight:

Old 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.

New 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='B',
     fields=[
         ('id', models.AutoField(auto_created=True, primary_key=True,
 serialize=False, verbose_name='ID')),
     ],
     options={
         'db_table': 'test_b',
         'managed': False,
     },
 ),
 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.

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29177#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/067.94b2a873fc7fdb319a5477c90314e257%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to