#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 [email protected].
To post to this group, send email to [email protected].
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